package com.heima.article.jobs;

import com.alibaba.fastjson.JSON;
import com.heima.article.service.HotArticleService;
import com.heima.common.constants.article.ArticleConstants;
import com.heima.common.constants.message.ArticleVisitStreamMess;
import com.heima.common.constants.message.UpdateArticleMess;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;

@Component
@Slf4j
public class UpdateHotArticleJob {

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    HotArticleService hotArticleService;


    @XxlJob("updateHotArticleJob")
    public ReturnT updateHotArticleHandler(String params){
        log.info("热文章分值更新 调度任务开始执行....");
        //1 获取redis 行为列表中待处理数据
        List<UpdateArticleMess> messList  = getUpdateArticleMesses();
        if (messList == null || messList.size() == 0) {
            log.info("热文章分值更新: 太冷清了 未产生任何文章行为 调度任务完成....");
            return ReturnT.SUCCESS;
        }

        //2 聚合运算文章的行为: 预期结果：文章对应的行为总次数
        List<ArticleVisitStreamMess> waitUpdateScoreData = getArticleVisitStreamMesses(messList);
        if ( waitUpdateScoreData.size() == 0) {
            log.info("热文章分值更新: 太冷清了 未产生任何文章行为 调度任务完成....");
            return ReturnT.SUCCESS;
        }


        //3 更新数据库文章行为数据，更新redis缓存
//        for (ArticleVisitStreamMess waitUpdateScoreDatum : waitUpdateScoreData) {
//            hotArticleService.updateApArticle(waitUpdateScoreDatum);
//        }
        waitUpdateScoreData.forEach(hotArticleService::updateApArticle);

        log.info("热文章分值更新 调度任务完成....");
        return ReturnT.SUCCESS;
    }

    /**
     * 聚合运算文章的行为: 预期结果：文章对应的行为总次数
     * @param messList
     * @return
     */
    private List<ArticleVisitStreamMess> getArticleVisitStreamMesses(List<UpdateArticleMess> messList) {

        List<ArticleVisitStreamMess> visitStreamMesses = new ArrayList<>();

        //1 分组 key:文章id value: 对应文章的行为列表
        Map<Long, List<UpdateArticleMess>> map = messList.stream()
                .collect(Collectors.groupingBy(UpdateArticleMess::getArticleId));
        //2 聚合运算
        map.forEach((articleId, articleMessList)->{

            //计算各个行为数据的和
            Optional<ArticleVisitStreamMess> reduceResult = articleMessList.stream()
                    .map(updateArticleMess -> {
                ArticleVisitStreamMess visitStreamMess = new ArticleVisitStreamMess();
                visitStreamMess.setArticleId(articleId);

                switch (updateArticleMess.getType()) {
                    case LIKES:
                        // 设置 点赞数量
                        visitStreamMess.setLike(updateArticleMess.getAdd());
                        break;
                    case VIEWS:
                        // 设置 阅读数量
                        visitStreamMess.setView(updateArticleMess.getAdd());
                        break;
                    case COMMENT:
                        // 设置 评论数量
                        visitStreamMess.setComment(updateArticleMess.getAdd());
                        break;
                    case COLLECTION:
                        // 设置 收藏数量
                        visitStreamMess.setCollect(updateArticleMess.getAdd());
                        break;
                }
                return visitStreamMess;
            })
                    // 累加：聚合计算
                    .reduce(new BinaryOperator<ArticleVisitStreamMess>() {
                        // 参数1：上次计算后的结果 参数2：待计算的对象
                        @Override
                        public ArticleVisitStreamMess apply(ArticleVisitStreamMess a1, ArticleVisitStreamMess a2) {
                            a1.setLike(a1.getLike() + a2.getLike());
                            a1.setView(a1.getView() + a2.getView());
                            a1.setComment(a1.getComment() + a2.getComment());
                            a1.setCollect(a1.getCollect() + a2.getCollect());
                            return a1;
                        }
                    });


            // 汇总结果
            if (reduceResult.isPresent()) {
                ArticleVisitStreamMess visitStreamMess = reduceResult.get();
                visitStreamMesses.add(visitStreamMess);
                log.info("热点文章 聚合计算结果  ===>{}" , visitStreamMess);
            }

        });

        //3 返回结果
        return visitStreamMesses;
    }

    /**
     *  获取redis 行为列表中待处理数据
     * @return
     *
     * Redis 管道命令：批量处理redis的指令
     *
     */
    private List<UpdateArticleMess> getUpdateArticleMesses() {
        ListOperations operations = redisTemplate.opsForList();

        // 获取集合的大小
        Long size = operations.size(ArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST);

        // 参数1：批量执行的回调方法， 参数2：对值序列化方法
        List result = redisTemplate.executePipelined(new RedisCallback<UpdateArticleMess>() {

            @Override
            public UpdateArticleMess doInRedis(RedisConnection connection) throws DataAccessException {
                //1 开启管道执行
                connection.openPipeline();
                //2 执行命令取 redis队列行为数据
                connection.lRange(ArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST.getBytes(), 0, (size - 1));
                //3 删除已处理的数据
                connection.lTrim(ArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST.getBytes(), size, -1);
                // 返回值必须为null
                return null;
            }
        }, RedisSerializer.string());


        if (result.size() > 0) {
            List<String>  listData = (List<String>) result.get(0);
           return   listData.stream()
                    .map(str -> JSON.parseObject(str, UpdateArticleMess.class))
                    .collect(Collectors.toList());
        }
        return null;
    }
}
